#include <DHT22.h>

#define DHT22_PIN 7 // Temp/humidity sensor data wire
#define e 2.7182818 // y'know... like... e!

int PinOut[4]={3,4,5,6};// Output pins (to station control SSR's)
int DelayTime[4];// Holds the delay between waterings for each station
int WaterTime=15;// Sets the number of seconds to run the pump for each station.
unsigned long TotalTime;// Used for calculation of cycle time
float Humidity;// Pretty obvious
float Temperature;// Ditto...
float StationTimer[4];// A timer for each station

DHT22 myDHT22(DHT22_PIN);// Setup a DHT22 instance with global scope

void setup(void)
{
  Serial.begin(9600);// start serial port
  for(int index = 0; index <= 3; index++){
    pinMode(PinOut[index],OUTPUT);//Set the pins as outputs
    digitalWrite(PinOut[index],HIGH);//Turn the watering stations on to start
    StationTimer[index] = WaterTime;//Set the number of cycles to stay on
  }
  TotalTime = 0;
}

void loop(void)
{ 
  int InValue[4];
  int NewDelayTime;
  int CycleTime;
  float InScaler[4];
  float UnscaledDelay;
  
  PollTempHumSensor();
  UnscaledDelay = 2160/((1.001-Humidity/100)*pow(e,17.502 * Temperature / (240.97 + Temperature)));
    //Calculate the unscaled delay between waterings based on an evaporation model.
  CycleTime = int(constrain(millis()-TotalTime,2000,4000));
    //Calculate the cycle time of the loop to be able to decrement the sleep timer
    //and constrain it to reasonable values since the millis function overflows every ~50d.
  TotalTime = TotalTime + CycleTime;//Update total time to calculate cycle time next loop
  Serial.println("Stn Val Scl  Dly  Tmr");
  for(int index = 0; index <= 3; index++){// For each watering station...
    Serial.print(index);
    Serial.print("   ");
    Serial.print(digitalRead(PinOut[index]));
    Serial.print("   ");
    InValue[index]=analogRead(index);//Get the value of the potentiometer input
    InScaler[index]=pow(2,((float)InValue[index]-512)/512);//Translate to a delay scaler
      //to go from half to double the unscaled value.  (1.0x is at the center)
    Serial.print(InScaler[index]);
    Serial.print(" ");
    NewDelayTime = int(constrain(InScaler[index]*UnscaledDelay,0,7200));
      //Scale the delay using the input pot and set the max time between waterings
    Serial.print(NewDelayTime);
    Serial.print(" ");
    if(digitalRead(PinOut[index]) == 0) { //if the station is sleeping
      StationTimer[index]=StationTimer[index]-DelayTime[index]+NewDelayTime; 
      //adjust the sleep counter based on the latest readings
      //This allows changes in temp or humidity to have a real time effect
      //on the sleep timer.  In other words, if a low temp and / or high humidity
      //caused the controller to calculate a long delay between waterings,
      //a change in conditions needs to be able to cause the controller to start
      //watering before the original delay time is over.
    }
    DelayTime[index] = NewDelayTime;//update the delay time
    StationTimer[index]=StationTimer[index]-(float)CycleTime/1000;//Decrement timer by cycle time.
    Serial.println(StationTimer[index]);
    if(StationTimer[index] <= 0) {//If the time is up...
       PinToggle(index);//toggle the pin value
    }
  }
  Serial.println("");//Skip a line
}

void PollTempHumSensor(){
  //Subroutine reads the temperature and humidity from the DHT22 sensor.
  DHT22_ERROR_t errorCode;
  delay(2000);
  Serial.print("Requesting data...");
  errorCode = myDHT22.readData();
  switch(errorCode)
  {
    case DHT_ERROR_NONE:
      Serial.print("Got Data ");
      Temperature=myDHT22.getTemperatureC();
      Serial.print(Temperature);
      Serial.print("C ");
      Humidity =myDHT22.getHumidity();
      Serial.print(Humidity);
      Serial.println("%");
      break;
    case DHT_ERROR_CHECKSUM:
      Serial.print("check sum error ");
      Temperature=myDHT22.getTemperatureC();
      Serial.print(Temperature);
      Serial.print("C ");
      Humidity =myDHT22.getHumidity();
      Serial.print(Humidity);
      Serial.println("%");
      break;
    case DHT_BUS_HUNG:
      Serial.println("BUS Hung ");
      break;
    case DHT_ERROR_NOT_PRESENT:
      Serial.println("Not Present ");
      break;
    case DHT_ERROR_ACK_TOO_LONG:
      Serial.println("ACK time out ");
      break;
    case DHT_ERROR_SYNC_TIMEOUT:
      Serial.println("Sync Timeout ");
      break;
    case DHT_ERROR_DATA_TIMEOUT:
      Serial.println("Data Timeout ");
      break;
    case DHT_ERROR_TOOQUICK:
      Serial.println("Polled to quick ");
      break;
  }  
}

void PinToggle(int Station){
  //Subroutine toggles the station status and resets the station timer
  int PinState;
  PinState = digitalRead(PinOut[Station]);// Read the station status.
  if (PinState == 1){ // If the station is on
    digitalWrite(PinOut[Station],LOW);// turn it off
    StationTimer[Station] = DelayTime[Station];// and set the station timer for the delay between watering
  }
  else {
    digitalWrite(PinOut[Station],HIGH);// Or if it's off, turn it on
    StationTimer[Station] = WaterTime;// and set the station timer for the time to stay on
  }
}
